import BaseMiddleware from './BaseMiddleware.js';
import { config } from '../config.js';
import fs from 'fs';
import path from 'path';
import { performance } from 'perf_hooks';

/**
 * 日志记录中间件 - 统一记录请求日志和应用程序日志
 */
export class LoggingMiddleware extends BaseMiddleware {
    constructor(options = {}) {
        super({
            priority: 10, // 高优先级，早期执行
            logToFile: config.log?.file || false,
            logToConsole: config.log?.console !== false,
            logDirectory: './logs',
            logLevel: config.log?.level || 'info',
            includeRequestBody: config.env === 'development',
            includeResponseBody: false,
            maxLogFileSize: 50 * 1024 * 1024, // 50MB
            logRotationDays: 7,
            sensitiveFields: ['password', 'token', 'secret', 'key', 'authorization'],
            skipPaths: ['/health', '/favicon.ico'],
            ...options
        });
        
        // 日志级别映射
        this.logLevels = {
            error: 0,
            warn: 1,
            info: 2,
            debug: 3,
            trace: 4
        };
        
        // 当前日志级别
        this.currentLogLevel = this.logLevels[this.options.logLevel] || 2;
        
        // 请求统计
        this.requestStats = {
            total: 0,
            byMethod: new Map(),
            byStatus: new Map(),
            byPath: new Map(),
            averageResponseTime: 0,
            totalResponseTime: 0
        };
        
        // 初始化日志目录
        if (this.options.logToFile) {
            this.initializeLogDirectory();
        }
    }

    /**
     * 执行日志记录逻辑
     * @param {Request} req - Express请求对象
     * @param {Response} res - Express响应对象
     * @param {Function} next - Express next函数
     */
    async execute(req, res, next) {
        // 跳过指定路径
        if (this.shouldSkipLogging(req)) {
            return next();
        }

        // 记录请求开始时间
        const startTime = performance.now();
        req.startTime = startTime;
        req.requestId = this.generateRequestId();
        
        // 记录请求日志
        await this.logRequest(req);
        
        // 拦截响应
        const originalSend = res.send;
        const originalJson = res.json;
        let responseBody = null;
        
        // 重写res.send方法
        res.send = function(body) {
            if (this.options.includeResponseBody) {
                responseBody = body;
            }
            return originalSend.call(this, body);
        }.bind(this);
        
        // 重写res.json方法
        res.json = function(body) {
            if (this.options.includeResponseBody) {
                responseBody = body;
            }
            return originalJson.call(this, body);
        }.bind(this);
        
        // 监听响应完成
        res.on('finish', async () => {
            const endTime = performance.now();
            const responseTime = endTime - startTime;
            
            // 更新统计信息
            this.updateRequestStats(req, res, responseTime);
            
            // 记录响应日志
            await this.logResponse(req, res, responseTime, responseBody);
        });
        
        next();
    }

    /**
     * 记录请求日志
     * @param {Request} req - Express请求对象
     */
    async logRequest(req) {
        const logEntry = {
            timestamp: new Date().toISOString(),
            level: 'info',
            type: 'request',
            requestId: req.requestId,
            method: req.method,
            url: req.originalUrl,
            path: req.path,
            query: req.query,
            params: req.params,
            headers: this.sanitizeHeaders(req.headers),
            body: this.options.includeRequestBody ? this.sanitizeObject(req.body) : undefined,
            ip: this.getClientIP(req),
            userAgent: req.get('User-Agent'),
            referer: req.get('Referer'),
            user: req.user ? {
                id: req.user.userId || req.user.id,
                roles: req.userRoles
            } : null
        };
        
        await this.writeLog('info', 'Incoming request', logEntry);
    }

    /**
     * 记录响应日志
     * @param {Request} req - Express请求对象
     * @param {Response} res - Express响应对象
     * @param {number} responseTime - 响应时间（毫秒）
     * @param {*} responseBody - 响应体
     */
    async logResponse(req, res, responseTime, responseBody) {
        const logLevel = this.getResponseLogLevel(res.statusCode);
        
        const logEntry = {
            timestamp: new Date().toISOString(),
            level: logLevel,
            type: 'response',
            requestId: req.requestId,
            method: req.method,
            url: req.originalUrl,
            statusCode: res.statusCode,
            responseTime: Math.round(responseTime * 100) / 100, // 保留2位小数
            contentLength: res.get('Content-Length'),
            body: this.options.includeResponseBody ? this.sanitizeObject(responseBody) : undefined,
            user: req.user ? {
                id: req.user.userId || req.user.id
            } : null
        };
        
        const message = `${req.method} ${req.originalUrl} - ${res.statusCode} - ${logEntry.responseTime}ms`;
        await this.writeLog(logLevel, message, logEntry);
    }

    /**
     * 写入日志
     * @param {string} level - 日志级别
     * @param {string} message - 日志消息
     * @param {Object} data - 日志数据
     */
    async writeLog(level, message, data = {}) {
        // 检查日志级别
        if (this.logLevels[level] > this.currentLogLevel) {
            return;
        }
        
        const logEntry = {
            timestamp: new Date().toISOString(),
            level: level.toUpperCase(),
            message,
            ...data
        };
        
        // 控制台输出
        if (this.options.logToConsole) {
            this.logToConsole(level, message, logEntry);
        }
        
        // 文件输出
        if (this.options.logToFile) {
            await this.logToFile(logEntry);
        }
    }

    /**
     * 控制台日志输出
     * @param {string} level - 日志级别
     * @param {string} message - 日志消息
     * @param {Object} logEntry - 日志条目
     */
    logToConsole(level, message, logEntry) {
        const timestamp = new Date().toLocaleTimeString();
        const prefix = `[${timestamp}][${level.toUpperCase()}][LoggingMiddleware]`;
        
        switch (level) {
            case 'error':
                console.error(prefix, message, config.env === 'development' ? logEntry : '');
                break;
            case 'warn':
                console.warn(prefix, message, config.env === 'development' ? logEntry : '');
                break;
            case 'debug':
            case 'trace':
                if (config.env === 'development') {
                    console.debug(prefix, message, logEntry);
                }
                break;
            default:
                console.log(prefix, message);
        }
    }

    /**
     * 文件日志输出
     * @param {Object} logEntry - 日志条目
     */
    async logToFile(logEntry) {
        try {
            const logFileName = `app-${new Date().toISOString().split('T')[0]}.log`;
            const logFilePath = path.join(this.options.logDirectory, logFileName);
            const logLine = JSON.stringify(logEntry) + '\n';
            
            // 检查文件大小并轮转
            await this.rotateLogFileIfNeeded(logFilePath);
            
            // 写入日志
            fs.appendFileSync(logFilePath, logLine);
        } catch (error) {
            console.error('[LoggingMiddleware] Failed to write log to file:', error);
        }
    }

    /**
     * 更新请求统计信息
     * @param {Request} req - Express请求对象
     * @param {Response} res - Express响应对象
     * @param {number} responseTime - 响应时间
     */
    updateRequestStats(req, res, responseTime) {
        this.requestStats.total++;
        
        // 按方法统计
        const method = req.method;
        this.requestStats.byMethod.set(method, (this.requestStats.byMethod.get(method) || 0) + 1);
        
        // 按状态码统计
        const status = res.statusCode;
        this.requestStats.byStatus.set(status, (this.requestStats.byStatus.get(status) || 0) + 1);
        
        // 按路径统计
        const path = req.path;
        this.requestStats.byPath.set(path, (this.requestStats.byPath.get(path) || 0) + 1);
        
        // 响应时间统计
        this.requestStats.totalResponseTime += responseTime;
        this.requestStats.averageResponseTime = this.requestStats.totalResponseTime / this.requestStats.total;
    }

    /**
     * 判断是否应该跳过日志记录
     * @param {Request} req - Express请求对象
     * @returns {boolean} 是否跳过
     */
    shouldSkipLogging(req) {
        return this.options.skipPaths.some(path => {
            if (typeof path === 'string') {
                return req.path === path;
            } else if (path instanceof RegExp) {
                return path.test(req.path);
            }
            return false;
        });
    }

    /**
     * 根据状态码获取日志级别
     * @param {number} statusCode - HTTP状态码
     * @returns {string} 日志级别
     */
    getResponseLogLevel(statusCode) {
        if (statusCode >= 500) {
            return 'error';
        } else if (statusCode >= 400) {
            return 'warn';
        } else {
            return 'info';
        }
    }

    /**
     * 清理对象，移除敏感信息
     * @param {Object} obj - 要清理的对象
     * @returns {Object} 清理后的对象
     */
    sanitizeObject(obj) {
        if (!obj || typeof obj !== 'object') {
            return obj;
        }
        
        const sanitized = Array.isArray(obj) ? [] : {};
        
        for (const [key, value] of Object.entries(obj)) {
            if (this.options.sensitiveFields.some(field => 
                key.toLowerCase().includes(field.toLowerCase())
            )) {
                sanitized[key] = '[REDACTED]';
            } else if (typeof value === 'object' && value !== null) {
                sanitized[key] = this.sanitizeObject(value);
            } else {
                sanitized[key] = value;
            }
        }
        
        return sanitized;
    }

    /**
     * 清理请求头
     * @param {Object} headers - 请求头对象
     * @returns {Object} 清理后的请求头
     */
    sanitizeHeaders(headers) {
        const sanitized = { ...headers };
        const sensitiveHeaders = ['authorization', 'cookie', 'x-api-key', 'x-auth-token'];
        
        sensitiveHeaders.forEach(header => {
            if (sanitized[header]) {
                sanitized[header] = '[REDACTED]';
            }
        });
        
        return sanitized;
    }

    /**
     * 初始化日志目录
     */
    initializeLogDirectory() {
        try {
            if (!fs.existsSync(this.options.logDirectory)) {
                fs.mkdirSync(this.options.logDirectory, { recursive: true });
                console.log(`✓ Log directory created: ${this.options.logDirectory}`);
            }
        } catch (error) {
            console.error('[LoggingMiddleware] Failed to create log directory:', error);
            this.options.logToFile = false;
        }
    }

    /**
     * 轮转日志文件（如果需要）
     * @param {string} logFilePath - 日志文件路径
     */
    async rotateLogFileIfNeeded(logFilePath) {
        try {
            if (!fs.existsSync(logFilePath)) {
                return;
            }
            
            const stats = fs.statSync(logFilePath);
            
            // 检查文件大小
            if (stats.size > this.options.maxLogFileSize) {
                const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
                const rotatedFileName = logFilePath.replace('.log', `-${timestamp}.log`);
                fs.renameSync(logFilePath, rotatedFileName);
                console.log(`✓ Log file rotated: ${rotatedFileName}`);
            }
            
            // 清理旧日志文件
            await this.cleanupOldLogs();
        } catch (error) {
            console.error('[LoggingMiddleware] Failed to rotate log file:', error);
        }
    }

    /**
     * 清理旧日志文件
     */
    async cleanupOldLogs() {
        try {
            const files = fs.readdirSync(this.options.logDirectory);
            const cutoffDate = new Date();
            cutoffDate.setDate(cutoffDate.getDate() - this.options.logRotationDays);
            
            for (const file of files) {
                if (file.endsWith('.log')) {
                    const filePath = path.join(this.options.logDirectory, file);
                    const stats = fs.statSync(filePath);
                    
                    if (stats.mtime < cutoffDate) {
                        fs.unlinkSync(filePath);
                        console.log(`✓ Old log file deleted: ${file}`);
                    }
                }
            }
        } catch (error) {
            console.error('[LoggingMiddleware] Failed to cleanup old logs:', error);
        }
    }

    /**
     * 生成请求ID
     * @returns {string} 请求ID
     */
    generateRequestId() {
        return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    }

    /**
     * 获取客户端IP地址
     * @param {Request} req - Express请求对象
     * @returns {string} IP地址
     */
    getClientIP(req) {
        return req.ip || 
               req.connection?.remoteAddress || 
               req.socket?.remoteAddress || 
               req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
               req.headers['x-real-ip'] ||
               'unknown';
    }

    /**
     * 获取请求统计信息
     * @returns {Object} 请求统计
     */
    getRequestStats() {
        return {
            total: this.requestStats.total,
            byMethod: Object.fromEntries(this.requestStats.byMethod),
            byStatus: Object.fromEntries(this.requestStats.byStatus),
            byPath: Object.fromEntries(this.requestStats.byPath),
            averageResponseTime: Math.round(this.requestStats.averageResponseTime * 100) / 100
        };
    }

    /**
     * 重置请求统计
     */
    resetRequestStats() {
        this.requestStats = {
            total: 0,
            byMethod: new Map(),
            byStatus: new Map(),
            byPath: new Map(),
            averageResponseTime: 0,
            totalResponseTime: 0
        };
        console.log('✓ Request statistics reset');
    }

    /**
     * 设置日志级别
     * @param {string} level - 日志级别
     */
    setLogLevel(level) {
        if (this.logLevels.hasOwnProperty(level)) {
            this.options.logLevel = level;
            this.currentLogLevel = this.logLevels[level];
            console.log(`✓ Log level set to: ${level}`);
        } else {
            console.warn(`Invalid log level: ${level}. Available levels: ${Object.keys(this.logLevels).join(', ')}`);
        }
    }

    /**
     * 记录自定义日志
     * @param {string} level - 日志级别
     * @param {string} message - 日志消息
     * @param {Object} data - 附加数据
     */
    async log(level, message, data = {}) {
        await this.writeLog(level, message, {
            type: 'custom',
            ...data
        });
    }

    // 便捷日志方法
    async error(message, data = {}) {
        await this.log('error', message, data);
    }

    async warn(message, data = {}) {
        await this.log('warn', message, data);
    }

    async info(message, data = {}) {
        await this.log('info', message, data);
    }

    async debug(message, data = {}) {
        await this.log('debug', message, data);
    }

    async trace(message, data = {}) {
        await this.log('trace', message, data);
    }
}

// 创建默认实例
export const loggingMiddleware = new LoggingMiddleware();

// 导出便捷方法
export const requestLogger = () => loggingMiddleware.getMiddleware();
export const logger = loggingMiddleware;

export default LoggingMiddleware;